﻿using System.Collections.Generic;
using IQIGame.Onigao.Framework.Serialization;
using IQIGame.Onigao.GamePlay;
using LFloatMath.Math;

namespace IQIGame.Onigao.Logic
{
    public class SkillEffectiveAction_ParamNum : ISerializeByteBuf
    {
        public long dataGuid;
        public int key;
        public int value;
        public int paramType;
        public SkillEffectiveAction_ParamBase dynmicParam;

        public void Serialize(ByteBuf _buf)
        {
            _buf.WriteLong(this.dataGuid);
            _buf.WriteInt(this.key);
            _buf.WriteInt(this.value);
            _buf.WriteInt(this.paramType);
            SerializeByteBufUtil.SerializeObject(_buf, this.dynmicParam);
        }

        public void DeSerialize(ByteBuf _buf)
        {
            this.dataGuid = _buf.ReadLong();
            this.key = _buf.ReadInt();
            this.value = _buf.ReadInt();
            this.paramType = _buf.ReadInt();
            this.dynmicParam = SkillEffectiveAction_ParamBase.Create(this.paramType);
            SerializeByteBufUtil.DeSerializeObject(_buf, ref this.dynmicParam);
        }

        public LFloat GetValue(Unit rAttacker, Unit rTarget, SkillEffectiveContext rContext)
        {
            if (this.key < 0 && this.dynmicParam == null)
            {
                return this.value.ToLFloat();
            }

            if (dynmicParam != null)
            {
                return dynmicParam.GetValueNum(rAttacker, rTarget, rContext);
            }
            Dictionary<long, Dictionary<int, TAny>> rResults = null;
            if (rTarget != null && !rContext.runtimeData.TryGetValue(rTarget, out rResults))
            {
                rResults = rContext.runtimeWithoutTarget;
            }

            if (rResults != null && rResults.TryGetValue(this.dataGuid, out var rResult))
            {
                rResult.TryGetValue(this.key, out var rValue);
                return TAnyHelper.GetAny<LFloat>(rValue);
            }

            return LFloat.zero;
        }
    }

    public class SkillEffectiveAction_ParamBool : ISerializeByteBuf
    {
        public long dataGuid;
        public int key;
        public bool value;
        public int paramType;
        public SkillEffectiveAction_ParamBase dynmicParam;

        public void Serialize(ByteBuf _buf)
        {
            _buf.WriteLong(this.dataGuid);
            _buf.WriteInt(this.key);
            _buf.WriteBool(this.value);
            _buf.WriteInt(this.paramType);
            SerializeByteBufUtil.SerializeObject(_buf, this.dynmicParam);
        }

        public void DeSerialize(ByteBuf _buf)
        {
            this.dataGuid = _buf.ReadLong();
            this.key = _buf.ReadInt();
            this.value = _buf.ReadBool();
            this.paramType = _buf.ReadInt();
            this.dynmicParam = SkillEffectiveAction_ParamBase.Create(this.paramType);
            SerializeByteBufUtil.DeSerializeObject(_buf, ref this.dynmicParam);
        }

        public bool GetValue(Unit rAttacker, Unit rTarget, SkillEffectiveContext rContext)
        {
            if (this.key < 0 && this.dynmicParam == null)
            {
                return this.value;
            }
            else if (this.dynmicParam != null)
            {
                return this.dynmicParam.GetValueBool(rAttacker, rTarget, rContext);
            }

            Dictionary<long, Dictionary<int, TAny>> rResults = null;
            if (rTarget != null && !rContext.runtimeData.TryGetValue(rTarget, out rResults))
            {
                rResults = rContext.runtimeWithoutTarget;
            }

            if (rResults != null && rResults.TryGetValue(this.dataGuid, out var rResult))
            {
                rResult.TryGetValue(this.key, out var rValue);
                return TAnyHelper.GetAny<bool>(rValue);
            }

            return false;
        }
    }

    public class SkillEffectiveAction_ParamBase : ISerializeByteBuf
    {
        public static SkillEffectiveAction_ParamBase Create(int nType)
        {
            switch ((EActionParamType)nType)
            {
                case EActionParamType.Formula:
                    return new SkillEffectiveAction_ParamFormula();
                case EActionParamType.SkillCorrect:
                    return new SkillEffectiveAction_ParamCorrect();
                case EActionParamType.DamageFormula:
                    return new SkillEffectiveAction_ParamDamageFormula();
                case EActionParamType.Value:
                case EActionParamType.BuffHandler:
                case EActionParamType.CureResponse:
                case EActionParamType.DamageResponse:
                    break;
            }

            return null;
        }

        public enum EActionParamType
        {
            Value,
            Formula,
            SkillCorrect,
            BuffHandler,
            DamageResponse,
            CureResponse,
            DamageFormula,
        }


        public virtual LFloat GetValueNum(Unit rAttacker, Unit rTarget, SkillEffectiveContext rContext)
        {
            return LFloat.zero;
        }

        public virtual bool GetValueBool(Unit rAttacker, Unit rTarget, SkillEffectiveContext rContext)
        {
            return false;
        }

        public virtual void Serialize(ByteBuf _buf)
        {
        }

        public virtual void DeSerialize(ByteBuf _buf)
        {
        }
    }

    public partial class SkillEffectiveAction_ParamFormula : SkillEffectiveAction_ParamBase
    {
        public int formula;

        public List<SkillEffectiveAction_ParamNum> nums = new List<SkillEffectiveAction_ParamNum>();
        public List<SkillEffectiveAction_ParamBool> bools = new List<SkillEffectiveAction_ParamBool>();

        public override LFloat GetValueNum(Unit rAttacker, Unit rTarget, SkillEffectiveContext rContext)
        {
            return this.Calculate<LFloat>(rAttacker, rTarget, rContext);
        }

        public override bool GetValueBool(Unit rAttacker, Unit rTarget, SkillEffectiveContext rContext)
        {
            return this.Calculate<bool>(rAttacker, rTarget, rContext);
        }

        private LFloat[] numParams = new LFloat[4];
        private bool[] boolParams = new bool[4];

        private T Calculate<T>(Unit rAttacker, Unit rTarget, SkillEffectiveContext rContext)
        {
            for (int i = 0; i < numParams.Length; i++)
            {
                if (this.nums.Count > i)
                {
                    numParams[i] = this.nums[i].GetValue(rAttacker, rTarget, rContext);
                }
                else
                {
                    numParams[i] = LFloat.zero;
                }
            }

            for (int i = 0; i < boolParams.Length; i++)
            {
                if (this.bools.Count > i)
                {
                    boolParams[i] = this.bools[i].GetValue(rAttacker, rTarget, rContext);
                }
                else
                {
                    boolParams[i] = false;
                }
            }

            switch (this.nums.Count)
            {
                case 0:
                    return Formula.Calculate<T, bool, bool, bool, bool>(this.formula, rAttacker, rTarget, rContext.skill, boolParams[0], boolParams[1], boolParams[2], boolParams[3]);
                case 1:
                    return Formula.Calculate<T, LFloat, bool, bool, bool>(this.formula, rAttacker, rTarget, rContext.skill, numParams[0], boolParams[0], boolParams[1], boolParams[2]);
                case 2:
                    return Formula.Calculate<T, LFloat, LFloat, bool, bool>(this.formula, rAttacker, rTarget, rContext.skill, numParams[0], numParams[1], boolParams[0], boolParams[1]);
                case 3:
                    return Formula.Calculate<T, LFloat, LFloat, LFloat, bool>(this.formula, rAttacker, rTarget, rContext.skill, numParams[0], numParams[1], numParams[2], boolParams[0]);
                case 4:
                    return Formula.Calculate<T, LFloat, LFloat, LFloat, LFloat>(this.formula, rAttacker, rTarget, rContext.skill, numParams[0], numParams[1], numParams[2], numParams[3]);
            }

            return default;
        }
    }

    public partial class SkillEffectiveAction_ParamDamageFormula : SkillEffectiveAction_ParamBase
    {
        public int formula;

        public List<SkillEffectiveAction_ParamNum> nums = new List<SkillEffectiveAction_ParamNum>();
        public List<SkillEffectiveAction_ParamBool> bools = new List<SkillEffectiveAction_ParamBool>();

        private LFloat[] numParams = new LFloat[2];
        private bool[] boolParams = new bool[2];

        public override LFloat GetValueNum(Unit rAttacker, Unit rTarget, SkillEffectiveContext rContext)
        {
            var rResult = Calculate<LFloat>(rAttacker, rTarget, rContext);
            return rResult;
        }

        public override bool GetValueBool(Unit rAttacker, Unit rTarget, SkillEffectiveContext rContext)
        {
            var rResult = Calculate<bool>(rAttacker, rTarget, rContext);
            return rResult;
        }

        private T Calculate<T>(Unit rAttacker, Unit rTarget, SkillEffectiveContext rContext)
        {
            for (int i = 0; i < numParams.Length; i++)
            {
                if (this.nums.Count > i)
                {
                    numParams[i] = this.nums[i].GetValue(rAttacker, rTarget, rContext);
                }
                else
                {
                    numParams[i] = LFloat.zero;
                }
            }

            for (int i = 0; i < boolParams.Length; i++)
            {
                if (this.bools.Count > i)
                {
                    boolParams[i] = this.bools[i].GetValue(rAttacker, rTarget, rContext);
                }
                else
                {
                    boolParams[i] = false;
                }
            }

            switch (this.nums.Count)
            {
                case 0:
                    return Formula.Calculate<T, LFloat, LFloat, bool, bool>(this.formula, rAttacker, rTarget, rContext.skill, rContext.correct.tempDamageModify, rContext.correct.tempHurtModify, boolParams[0], boolParams[1]);
                case 1:
                    return Formula.Calculate<T, LFloat, LFloat, LFloat, bool>(this.formula, rAttacker, rTarget, rContext.skill, rContext.correct.tempDamageModify, rContext.correct.tempHurtModify, numParams[0], boolParams[0]);
                case 2:
                    return Formula.Calculate<T, LFloat, LFloat, LFloat, LFloat>(this.formula, rAttacker, rTarget, rContext.skill, rContext.correct.tempDamageModify, rContext.correct.tempHurtModify, numParams[0], numParams[1]);
            }

            return default;
        }
    }

    public partial class SkillEffectiveAction_ParamCorrect : SkillEffectiveAction_ParamBase
    {
        public int type;

        public override LFloat GetValueNum(Unit rAttacker, Unit rTarget, SkillEffectiveContext rContext)
        {
            var nResult = LFloat.zero;
            if (rContext.param is SkillCorrect rCorrect)
            {
                switch ((EParamCorrectType)type)
                {
                    case EParamCorrectType.TempDamageModify: // 临时伤害加成
                        nResult = rCorrect.tempDamageModify;
                        break;
                    case EParamCorrectType.TempHurtModify: // 临时伤害减免
                        nResult = rCorrect.tempHurtModify;
                        break;
                    case EParamCorrectType.TargetNum: // 目标数量
                        nResult = (rCorrect.select?.targets.Count ?? 0).ToLFloat();
                        break;
                }
            }

            return nResult;
        }

        public enum EParamCorrectType
        {
            TempDamageModify,
            TempHurtModify,
            TargetNum,
        }
    }
}